package utils

import (
	"context"
	"errors"
	"io"
	"os/exec"

	"gitee.com/lihaiping1603/utils/log"
)

var (
	ErrProcessContextDone = errors.New("process run but upper exit")
)

type SubProcess struct {
	process *exec.Cmd
}

func NewSubProcess() *SubProcess {
	return &SubProcess{}
}

func (sub *SubProcess) Run(ctx context.Context, ow io.Writer, ew io.Writer, cmd string, args ...string) error {
	return sub.RunWithDir(ctx, ow, ew, "", cmd, args...)
}

func (sub *SubProcess) RunWithDir(ctx context.Context, ow io.Writer, ew io.Writer, dir string, cmd string, args ...string) error {
	if nil != sub.process {
		sub.process.Process.Kill()
	}
	//构造
	cmd_process := exec.CommandContext(ctx, cmd, args...)
	if dir != "" {
		cmd_process.Dir = dir
	}

	if ow != nil {
		cmd_process.Stdout = ow
	}
	if ew != nil {
		cmd_process.Stderr = ew
	}
	//cmd_process.Stdout = &sub.outBuf
	//cmd_process.Stderr = &sub.errBuf
	//启动执行
	err := cmd_process.Start()
	if err != nil {
		log.Error("start run:%s failed,error:%s", cmd_process.String(), err.Error())
		return err
	}
	sub.process = cmd_process
	return nil
}

func (sub *SubProcess) Wait(ctx context.Context) error {
	if nil == sub.process {
		log.Warn("the process does not exist")
		return nil
	}
	cmd_process := sub.process
	//创建一个channel
	done := make(chan error)
	go func() {
		done <- cmd_process.Wait()
		close(done)
	}()

	select {
	case <-ctx.Done():
		if err := cmd_process.Process.Kill(); err != nil {
			log.Error("failed to kill:%s,error:%s", cmd_process.Path, err.Error())
		}
		go func() {
			//防止因为kill以后,wait goroutine里面的done因为没有接受，导致无法写数据到done中
			<-done //allow wait goroutine to exit
		}()
		log.Info("process:%s killed,because upper exit", cmd_process.Path)
		return ErrProcessContextDone
	case err := <-done:
		if err != nil {
			return err
		}
	}
	return nil
}

func (sub *SubProcess) RunWait(ctx context.Context, ow io.Writer, ew io.Writer, cmd string, args ...string) error {
	err := sub.Run(ctx, ow, ew, cmd, args...)
	if err != nil {
		return err
	}
	return sub.Wait(ctx)
}
